Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Feat/login #23

Merged
merged 9 commits into from
Nov 3, 2023
Merged

[WIP] Feat/login #23

merged 9 commits into from
Nov 3, 2023

Conversation

usatie
Copy link
Owner

@usatie usatie commented Nov 3, 2023

Summary by CodeRabbit

  • New Features
    • Added password field to User model and updated user creation and update methods to handle password information.
    • Introduced UserCard component for displaying, updating, and deleting user information.
    • Added UserCreateForm component for creating new users.
  • Bug Fixes
    • Updated file paths in Makefile for the prettier command.
    • Modified import paths for various components across the frontend.
  • Refactor
    • Moved form submission and API request handling from signup page to UserCreateForm component.
  • Style
    • Minor style changes in configuration files for consistency.

Copy link
Contributor

coderabbitai bot commented Nov 3, 2023

Warning

Rate Limit Exceeded

@usatie has exceeded the limit for the number of files or commits that can be reviewed per hour. Please wait 4 minutes and 49 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per repository.
Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
Please see our FAQ for further information.

Commits Files that changed from the base of the PR and between 3a9c090 and e41d9d6.

Walkthrough

The changes in this update focus on adding password functionality to the User model and updating the frontend and backend accordingly. The backend now includes a new password field in the User model and updates the User controller to handle password information. The frontend introduces new components for user creation and management, incorporating the password field. Additionally, there are minor changes to configuration files and formatting.

Changes

File(s) Summary
Makefile Updated file paths for the prettier command.
backend/prisma/migrations/20231103070602_user_password/migration.sql, backend/prisma/schema.prisma Added a new password field to the User model.
backend/src/user/user.controller.ts Modified create and update methods to handle the password field.
frontend/app/layout.tsx, frontend/app/ui/Nav.tsx Updated import paths for components.
frontend/app/ui/user/card.tsx, frontend/app/ui/user/create-form.tsx Introduced new components for user management and creation, including the password field.
frontend/app/user/[id]/page.tsx, frontend/app/user/signup/page.tsx Updated import paths and replaced form handling with new components.
frontend/lib/client-actions.ts Added a new createUser function for user creation requests.
frontend/next.config.js, frontend/postcss.config.js, frontend/tailwind.config.ts Minor formatting and configuration changes.

Poem

🐇 Hopping through the code, a rabbit's delight,

Adding passwords, making the app just right.

🍂 As the leaves fall, we code away,

Celebrating the changes we made today.

🎉 With each new line, a cheer, a toast,

To the code we love the most.

🌍 From every culture, every land,

We code together, hand in hand.


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • If you reply to a review comment from CodeRabbit, the bot will automatically respond.
  • To engage with CodeRabbit bot directly around the specific lines of code in the PR, mention @coderabbitai in your review comment
  • Note: Review comments are made on code diffs or files, not on the PR overview.
  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai help to get help.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.

Note: For conversation with the bot, please use the review comments on code diffs or files.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.json

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 5

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 9cce0e9 and 8d1c254.
Files ignored due to filter (1)
  • frontend/components.json
Files selected for processing (14)
  • Makefile (1 hunks)
  • backend/prisma/migrations/20231103070602_user_password/migration.sql (1 hunks)
  • backend/prisma/schema.prisma (1 hunks)
  • backend/src/user/user.controller.ts (2 hunks)
  • frontend/app/layout.tsx (1 hunks)
  • frontend/app/ui/Nav.tsx (1 hunks)
  • frontend/app/ui/user/card.tsx (1 hunks)
  • frontend/app/ui/user/create-form.tsx (1 hunks)
  • frontend/app/user/[id]/page.tsx (1 hunks)
  • frontend/app/user/signup/page.tsx (2 hunks)
  • frontend/lib/client-actions.ts (1 hunks)
  • frontend/next.config.js (1 hunks)
  • frontend/postcss.config.js (1 hunks)
  • frontend/tailwind.config.ts (2 hunks)
Files skipped from review due to trivial changes (8)
  • Makefile
  • frontend/app/layout.tsx
  • frontend/app/ui/Nav.tsx
  • frontend/app/user/[id]/page.tsx
  • frontend/lib/client-actions.ts
  • frontend/next.config.js
  • frontend/postcss.config.js
  • frontend/tailwind.config.ts
Additional comments: 9
backend/src/user/user.controller.ts (2)
  • 17-23: The create method now accepts a password field in the userData object. Ensure that the password is being hashed before being stored in the database for security reasons. If not, consider hashing the password in the UserService.create method.

  • 35-41: The update method now accepts a password field in the userData object. If the password is being updated, it should be hashed before being stored in the database. If not, consider hashing the password in the UserService.update method.

frontend/app/user/signup/page.tsx (3)
  • 1-3: The import statements are clean and organized. Good use of destructuring to import multiple components from the same module.

  • 10-10: The Form component is imported correctly from the @/app/ui/user/create-form module.

  • 12-20: The SignUp function correctly renders the Form component within the CardContent component. The code is clean and easy to read.

backend/prisma/migrations/20231103070602_user_password/migration.sql (1)
  • 1-8: Adding a NOT NULL column to a table that may already have data can cause issues. If the "User" table is not empty, this operation will fail because the new "password" column won't have values for existing rows. Consider adding a default value or handling this in a way that accommodates existing data.
- ALTER TABLE "User" ADD COLUMN     "password" TEXT NOT NULL;
+ ALTER TABLE "User" ADD COLUMN     "password" TEXT NOT NULL DEFAULT 'default_password';

Please replace 'default_password' with an appropriate default value. After the migration, ensure to update these default passwords to actual user passwords or prompt users to reset their password.

frontend/app/ui/user/create-form.tsx (2)
  • 16-24: Ensure that the createUser function is properly handling and validating these input values. It's important to validate user input on both the client and server side for security reasons. For example, you should check that the email is in a valid format and the password meets your security requirements (e.g., minimum length, contains certain types of characters, etc.).

  • 26-26: The button type is correctly set to "submit", which means clicking the button will submit the form. Ensure that the form submission is being handled correctly.

frontend/app/ui/user/card.tsx (1)
  • 1-1: The "use client" directive is not recognized in JavaScript or TypeScript. If it's a custom directive, ensure it's implemented correctly. If it's a typo, it should be removed.
- "use client";

Comment on lines 14 to 18
id Int @id @default(autoincrement())
email String @unique
name String?
password String
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The password field is being stored as a plain String. This is a serious security issue as it exposes sensitive user data. Passwords should be hashed before being stored in the database. Consider using a library like bcrypt to hash passwords.

- password String
+ password String @db.VarChar(60)

The @db.VarChar(60) directive is used to ensure the hashed password fits into the database column. Bcrypt, for example, generates a string of length 60.


Commitable suggestion (Beta)
Suggested change
id Int @id @default(autoincrement())
email String @unique
name String?
password String
}
id Int @id @default(autoincrement())
email String @unique
name String?
- password String
+ password String @db.VarChar(60)
}


export default function UserCreateForm() {
return (
<form action={createUser}>
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The action attribute in a form is typically used to specify the URL where the form data is sent on submission. Here, it seems you're trying to assign a JavaScript function to it, which won't work as expected. Instead, you should handle form submission using the onSubmit event. Remember to prevent the default form submission behavior using event.preventDefault() to avoid a page refresh.

- <form action={createUser}>
+ <form onSubmit={(event) => {event.preventDefault(); createUser();}}>

Commitable suggestion (Beta)
Suggested change
<form action={createUser}>
<form onSubmit={(event) => {event.preventDefault(); createUser();}}>

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is React's new feature. You should look into it.
https://react.dev/reference/react-dom/components/form

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@usatie Thank you for pointing out the new feature in React. I appreciate your input and the link you provided. I've reviewed the documentation and it seems you're correct. The action attribute can now accept a function in React, which is a departure from traditional HTML form behavior. This is a great example of how frameworks like React can enhance and simplify web development. I apologize for the oversight and appreciate your correction.

@@ -0,0 +1,120 @@
"use client";

import { useRouter } from "next/navigation";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement for useRouter is incorrect. It should be imported from "next/router", not "next/navigation".

- import { useRouter } from "next/navigation";
+ import { useRouter } from "next/router";

Commitable suggestion (Beta)
Suggested change
import { useRouter } from "next/navigation";
import { useRouter } from "next/router";

Comment on lines 24 to 52
async function updateUser(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const { id, ...updateData } = Object.fromEntries(
new FormData(event.currentTarget),
);
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updateData),
},
);
const data = await res.json();
if (!res.ok) {
toast({
title: res.status + " " + res.statusText,
description: data.message,
});
} else {
toast({
title: "Success",
description: "User updated successfully.",
});
router.push("/user");
router.refresh();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updateUser function does not handle network errors. If the fetch request fails due to network issues, the function will throw an unhandled promise rejection. Consider adding a try-catch block to handle these errors.

- async function updateUser(event: React.FormEvent<HTMLFormElement>) {
+ async function updateUser(event: React.FormEvent<HTMLFormElement>) {
+   try {
    event.preventDefault();
    const { id, ...updateData } = Object.fromEntries(
      new FormData(event.currentTarget),
    );
    const res = await fetch(
      `${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
      {
        method: "PATCH",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(updateData),
      },
    );
    const data = await res.json();
    if (!res.ok) {
      toast({
        title: res.status + " " + res.statusText,
        description: data.message,
      });
    } else {
      toast({
        title: "Success",
        description: "User updated successfully.",
      });
      router.push("/user");
      router.refresh();
    }
+   } catch (error) {
+     toast({
+       title: "Error",
+       description: "An error occurred while updating the user.",
+     });
+   }
  }

Commitable suggestion (Beta)
Suggested change
async function updateUser(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const { id, ...updateData } = Object.fromEntries(
new FormData(event.currentTarget),
);
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updateData),
},
);
const data = await res.json();
if (!res.ok) {
toast({
title: res.status + " " + res.statusText,
description: data.message,
});
} else {
toast({
title: "Success",
description: "User updated successfully.",
});
router.push("/user");
router.refresh();
}
async function updateUser(event: React.FormEvent<HTMLFormElement>) {
try {
event.preventDefault();
const { id, ...updateData } = Object.fromEntries(
new FormData(event.currentTarget),
);
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updateData),
},
);
const data = await res.json();
if (!res.ok) {
toast({
title: res.status + " " + res.statusText,
description: data.message,
});
} else {
toast({
title: "Success",
description: "User updated successfully.",
});
router.push("/user");
router.refresh();
}
} catch (error) {
toast({
title: "Error",
description: "An error occurred while updating the user.",
});
}
}

Comment on lines 54 to 75
async function deleteUser(event: React.SyntheticEvent) {
event.preventDefault();
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
{
method: "DELETE",
},
);
const data = await res.json();
if (!res.ok) {
toast({
title: res.status + " " + res.statusText,
description: data.message,
});
} else {
toast({
title: "Success",
description: "User deleted successfully.",
});
router.push("/user");
router.refresh();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the updateUser function, the deleteUser function also does not handle network errors. Consider adding a try-catch block to handle these errors.

- async function deleteUser(event: React.SyntheticEvent) {
+ async function deleteUser(event: React.SyntheticEvent) {
+   try {
    event.preventDefault();
    const res = await fetch(
      `${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
      {
        method: "DELETE",
      },
    );
    const data = await res.json();
    if (!res.ok) {
      toast({
        title: res.status + " " + res.statusText,
        description: data.message,
      });
    } else {
      toast({
        title: "Success",
        description: "User deleted successfully.",
      });
      router.push("/user");
      router.refresh();
    }
+   } catch (error) {
+     toast({
+       title: "Error",
+       description: "An error occurred while deleting the user.",
+     });
+   }
  }

Commitable suggestion (Beta)
Suggested change
async function deleteUser(event: React.SyntheticEvent) {
event.preventDefault();
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
{
method: "DELETE",
},
);
const data = await res.json();
if (!res.ok) {
toast({
title: res.status + " " + res.statusText,
description: data.message,
});
} else {
toast({
title: "Success",
description: "User deleted successfully.",
});
router.push("/user");
router.refresh();
}
async function deleteUser(event: React.SyntheticEvent) {
try {
event.preventDefault();
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/user/${user.id}`,
{
method: "DELETE",
},
);
const data = await res.json();
if (!res.ok) {
toast({
title: res.status + " " + res.statusText,
description: data.message,
});
} else {
toast({
title: "Success",
description: "User deleted successfully.",
});
router.push("/user");
router.refresh();
}
} catch (error) {
toast({
title: "Error",
description: "An error occurred while deleting the user.",
});
}
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 8d1c254 and e7e1214.
Files selected for processing (3)
  • frontend/app/ui/user/create-form.tsx (1 hunks)
  • frontend/app/user/signup/page.tsx (2 hunks)
  • frontend/lib/client-actions.ts (1 hunks)
Files skipped from review due to trivial changes (1)
  • frontend/lib/client-actions.ts
Additional comments: 3
frontend/app/user/signup/page.tsx (1)
  • 7-22: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [1-22]

The changes look good. The Form component from @/app/ui/user/create-form is now being used for user creation, which is a good practice for code reusability and maintainability. Ensure that the Form component handles all the necessary form submission and API request logic that was previously handled in this file.

frontend/app/ui/user/create-form.tsx (2)
  • 10-40: The form action is set to createUser but it's not clear how the form data is passed to this function. Typically, form data is collected using form event handlers in React. Consider updating the form to handle the onSubmit event and pass the form data to createUser.
- <form action={createUser}>
+ <form onSubmit={(event) => {
+   event.preventDefault();
+   const formData = new FormData(event.target);
+   createUser({
+     name: formData.get('name'),
+     email: formData.get('email'),
+     password: formData.get('password'),
+   });
+ }}>

16, 22, 30:
Ensure that the Input component properly handles the name prop to associate it with the form data.

  • 30-34: The placeholder suggests a password requirement of at least 6 characters. Ensure that this requirement is enforced on the server-side during user creation.

- Move client-actions.ts to app/lib/client-actions.ts
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e7e1214 and 3a9c090.
Files selected for processing (4)
  • frontend/app/layout.tsx (1 hunks)
  • frontend/app/lib/client-actions.ts (1 hunks)
  • frontend/app/ui/nav.tsx (1 hunks)
  • frontend/app/ui/user/create-form.tsx (1 hunks)
Files skipped from review due to trivial changes (2)
  • frontend/app/layout.tsx
  • frontend/app/lib/client-actions.ts
Additional comments: 3
frontend/app/ui/nav.tsx (2)
  • 1-3: The import statements look good. They are importing necessary components for the Nav function.

  • 5-35: The Nav function is well-structured and follows best practices for creating a navigation bar in React. It uses the Image component to display a logo, the Link component to create clickable links, and the ModeToggle component to toggle between light and dark modes. The use of the header, nav, and ul HTML elements is semantically correct.

frontend/app/ui/user/create-form.tsx (1)
  • 12-12: The previous comment about the incorrect use of the action attribute in the form is still valid. The action attribute should be a URL where the form data is sent on submission. Here, it seems you're trying to assign a JavaScript function to it, which won't work as expected. Instead, you should handle form submission using the onSubmit event. Remember to prevent the default form submission behavior using event.preventDefault() to avoid a page refresh.
- <form action={createUser}>
+ <form onSubmit={(event) => {event.preventDefault(); createUser();}}>

/>
</li>
<li className="flex gap-8 items-center">
<Link href="/" className="">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Link component does not accept a className prop. If you want to add a class to the link, you should wrap the link text in an a tag and add the class to the a tag.

- <Link href="/" className="">
-   Home
- </Link>
+ <Link href="/">
+   <a className="">Home</a>
+ </Link>

Commitable suggestion (Beta)
Suggested change
<Link href="/" className="">
<Link href="/">
<a className="">Home</a>
</Link>

Comment on lines 26 to 28
<Link href="/playground/pong.html" target="_blank">
Pong
</Link>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Link component does not accept a target prop. If you want to open the link in a new tab, you should wrap the link text in an a tag and add the target attribute to the a tag.

- <Link href="/playground/pong.html" target="_blank">
-   Pong
- </Link>
+ <Link href="/playground/pong.html">
+   <a target="_blank">Pong</a>
+ </Link>

Commitable suggestion (Beta)
Suggested change
<Link href="/playground/pong.html" target="_blank">
Pong
</Link>
<Link href="/playground/pong.html">
<a target="_blank">Pong</a>
</Link>

Comment on lines +10 to +40
export default function UserCreateForm() {
return (
<form action={createUser}>
<div className="grid w-full items-center gap-8">
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">User Name</Label>
<Input id="name" type="text" name="name" placeholder="e.g. nop" />
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
name="email"
placeholder="e.g. [email protected]"
/>
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
name="password"
placeholder="Must have at least 6 characters"
/>
</div>
<Button type="submit">Sign Up</Button>
</div>
</form>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The form lacks state management for the input fields. You should use React's useState hook to manage the state of each input field. When the form is submitted, these state variables should be passed to the createUser function.

export default function UserCreateForm() {
  const [name, setName] = React.useState("");
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");

  const handleSubmit = (event) => {
    event.preventDefault();
    createUser({ name, email, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <div className="grid w-full items-center gap-8">
        <div className="flex flex-col space-y-1.5">
          <Label htmlFor="name">User Name</Label>
          <Input id="name" type="text" name="name" placeholder="e.g. nop" value={name} onChange={(e) => setName(e.target.value)} />
        </div>
        <div className="flex flex-col space-y-1.5">
          <Label htmlFor="email">Email</Label>
          <Input id="email" type="email" name="email" placeholder="e.g. [email protected]" value={email} onChange={(e) => setEmail(e.target.value)} />
        </div>
        <div className="flex flex-col space-y-1.5">
          <Label htmlFor="password">Password</Label>
          <Input id="password" type="password" name="password" placeholder="Must have at least 6 characters" value={password} onChange={(e) => setPassword(e.target.value)} />
        </div>
        <Button type="submit">Sign Up</Button>
      </div>
    </form>
  );
}

@usatie
Copy link
Owner Author

usatie commented Nov 3, 2023

  1. User情報にpasswordを追加しました
  2. next-learnでやったディレクトリ・ファイル構造にほぼ変更しました。
    (shadcnに関する部分だけはshadcnにいくつかハードコードされてしまっているディレクトリ構造の問題があるため、残しています。)

まだpasswordを生で出し入れしてたりするので次はbcryptでハッシュ化してから保存するなどします。だけどそれ以外のこともディレクトリ構造をいじったりしたので早めにマージしてしまいたく、WIPのPRも出ていないようですし、PR閉じてしまいます〜

@usatie usatie merged commit 3fa414d into main Nov 3, 2023
1 check passed
@usatie usatie deleted the feat/login branch November 3, 2023 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant